package com.ocom.netty.mj.handler;

import cn.hutool.core.util.HexUtil;
import com.ocom.netty.mj.ropo.TcpServiceRopo;
import com.ocom.netty.mj.service.MachTcpService;
import com.ocom.netty.mj.util.CRC161;
import com.ocom.netty.mj.util.DES;
import com.ocom.netty.mj.util.NumConvertUtil;
import com.ocom.redis.util.RedisUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static io.netty.handler.codec.stomp.StompHeaders.HEART_BEAT;

/**
 * 数据接受Handler
 */
@Component
@Slf4j
@ChannelHandler.Sharable
public class DataAcceptHandler extends ChannelInboundHandlerAdapter {


    @Value("${opendoor.host}")
    String server;

    @Resource
    RedisUtil redisUtil;

    Logger logger= LoggerFactory.getLogger(DataAcceptHandler.class);


    public static Map<String, ChannelHandlerContext> ctxMap = new ConcurrentHashMap<String, ChannelHandlerContext>(16);


    @Autowired
    private MachTcpService machTcpService;


    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        ctx.fireChannelActive();
        if (log.isDebugEnabled()) {
            log.debug(ctx.channel().remoteAddress() + "");
        }
        System.out.println("tcp链接接入");
        System.out.println("tcp链接接入： "+ ctx.channel().remoteAddress());
        ctx.writeAndFlush("Your channel is build " +"\r\n");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

//        System.out.println(ctx);

        String messageData=objectToHexStr(msg);
        logger.info("Client,接收到服务端发来的消息:" + messageData);

        byte[] bytes=NumConvertUtil.hexStringToByte(messageData);
        if (bytes.length < 33) {
            logger.info("恶意链接 orgin："+ctx.channel().remoteAddress());
            logger.error("恶意链接");
            ctx.close();
            return;
        }

        //判断开头
        String stxLocal=NumConvertUtil.byteToHex(bytes[0],bytes[1]);
        if(!stxLocal.equals("caac")){
       //     log.error("数据异常-开头不对");
            ctx.close();
            return;
        }

        //判断结尾
        String extLocal=NumConvertUtil.byteToHex(bytes[bytes.length-2],bytes[bytes.length-1]);
        if(!extLocal.equals("acca")){
      //      logger.error("数据异常-结尾不对");
            ctx.close();
            return;
        }

        //判断长度
        String strLengthLocal=NumConvertUtil.byteToHex(bytes[2],bytes[3]);
        //16转10
        Integer b=Integer.parseInt(strLengthLocal,16);
        if(b+6!=bytes.length){
//            System.out.println(b);
//            System.out.println(bytes.length);
            logger.error("数据异常-数据长度错误1");
      //      ctx.close();
            return;
        }
        //判断命令长度 参数长度
        String commandLength=NumConvertUtil.byteToHex(bytes[21],bytes[22]);
        Integer length=Integer.parseInt(commandLength,16);

        if(length+33!=bytes.length){
            logger.error("数据异常-数据长度错误2");
       //     ctx.close();
            return;
        }

        //校验数据:
        String yanqian=messageData.substring(8,messageData.length()-20);
//        System.out.println("yanqian："+yanqian);
        //crc16
        String crcHexStr= CRC161.getCRC3(NumConvertUtil.hexStringToByte(yanqian));
//        System.out.println(crcHexStr);
        if(crcHexStr.length()==2){
            crcHexStr+="00";
        }
//        if(crcHexStr.length()==3){
//            StringBuilder stringBuilder=new StringBuilder(crcHexStr);
//            stringBuilder.insert(2,"0");
//            crcHexStr=stringBuilder.toString();
//        }
//        System.out.println("crc:"+crcHexStr);
        //异或和
        String yhh=NumConvertUtil.hexYihuo(yanqian);
        if(yhh.length()==1){
            yhh="0"+yhh;
        }
//        System.out.println("yhh:"+yhh);
        //累加和
        String ljh=NumConvertUtil.hexLeijia(yanqian);
        if(ljh.length()==1){
            ljh+="0";
        }
//        System.out.println("ljh:"+ljh);
        //包号
        String packageNum=NumConvertUtil.byteToHex(bytes[11],bytes[12],bytes[13],bytes[14]);

        //验签
        String signData=packageNum+crcHexStr+yhh+ljh;

        //des加密
        DES des = new DES(NumConvertUtil.hexStringToByte("0044C3D1237CA268"));
        byte[] bytes1 = des.encrypt(NumConvertUtil.hexStringToByte(signData));
//        System.out.println("longSign:"+NumConvertUtil.bytesToHexString(bytes1));
        String tcpSign=NumConvertUtil.bytesToHexString(bytes1).substring(0, 16);

        byte[] signBytes=new byte[8];
        for(int i=bytes.length-10;i<bytes.length-2;i++){
            signBytes[i-(bytes.length-10)]=bytes[i];
        }
        //tcp上的sign
        String sign=NumConvertUtil.byteToHex(signBytes);

        if(!tcpSign.equals(sign)){
            System.out.println(sign);
            System.out.println(tcpSign);
            logger.error("验签失败");
            ctx.close();
            return;
        }

        TcpServiceRopo tcpServiceRopo=new TcpServiceRopo(bytes);
        System.out.println(tcpServiceRopo);
        logger.info("收到 tcpServiceRopo："+tcpServiceRopo.toString());

        //记录tcp 长链接
        ctxMap.put(tcpServiceRopo.getMachId(),ctx);

        redisUtil.set("Mach#server#"+tcpServiceRopo.getMachId(),server,31536000l);

        switch (tcpServiceRopo.getCommandType()) {
            //签到(0x0001)
            case "0001" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(machTcpService.sign(tcpServiceRopo)));
                break;
            // 心跳 (0x0003)
            case "0003" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(machTcpService.heartbeat(tcpServiceRopo)));
                break;
            //门禁离线开门数据上传（0x8001）
            case "8001" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(machTcpService.uploadData(tcpServiceRopo)));
                break;
            //卡认证开门命令（0x8003）
            case "8003" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(machTcpService.openDoorCommand(tcpServiceRopo)));
                break;
            //门禁码在线/离线消费（0x8004）
            case "8004" :
            case "8005" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(machTcpService.doorPay(tcpServiceRopo)));
                break;
            //获取门禁权限组（0x8008）
            case "8008" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(machTcpService.doorAuth(tcpServiceRopo)));
                break;
            //获取门禁日期时区表（0x8009）
            case "8009" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(machTcpService.doorTime(tcpServiceRopo)));
                break;
            //固件更新续传 (0xcc00)
            case "cc00" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(machTcpService.updateMach(tcpServiceRopo)));
                break;
            //固件更新结果通知续传(0xdc00)
            case "dc00" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(machTcpService.updateMachSequel(tcpServiceRopo)));
                break;
            //云卡通2.0黑/白名单全量/增量下传命令（0x0168/0x0170）
            case "0168" :
            case "0170" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(machTcpService.whiteRecode(tcpServiceRopo)));
                break;
            //云卡通2.0黑/白名单全量/增量下传结果通知（0x1168/0x1170）
            case "1168" :
            case "1170" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(machTcpService.whiteRecodeResult(tcpServiceRopo)));
                break;
            //上报设置二维码（0x8100）
            case "8100" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(machTcpService.qrCode(tcpServiceRopo)));
                break;
            //离线码秘钥更新通知(0x1105)
            case "0105" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(machTcpService.updateKey(tcpServiceRopo)));
                break;
            //离线码秘钥更新通知(0x1105)
            case "1105" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(machTcpService.updateKeyResult(tcpServiceRopo)));
                break;
            //获取门禁日期时区表结果（0x8109）
            case "8109" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(machTcpService.doorTimeResult(tcpServiceRopo)));
                break;
            case "0043" :
                logger.info("已经开门");
//                ctx.writeAndFlush(Unpooled.wrappedBuffer(machTcpService.distanceOpenDoor(tcpServiceRopo)));
                break;
            default:
//                log.error("无效请求参数");
//                ctx.close();
                logger.info("无效请求参数");
//                ctx.writeAndFlush(Unpooled.wrappedBuffer(machTcpService.sign(tcpServiceRopo)));

        }



    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error(cause.getMessage(), cause);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx){
        removeChannelMap(ctx);
        log.debug(ctx.channel().remoteAddress() + "inactive");
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
//        if (evt instanceof IdleStateEvent) {
//            log.error("timeout");
//            ctx.channel().close();
//        }
        if (evt instanceof IdleStateEvent){
            IdleStateEvent idleStateEvent = (IdleStateEvent) evt ;
            if (idleStateEvent.state() == IdleState.READER_IDLE){
                log.info("已经5秒没有收到信息！");
                //向客户端发送消息
                ctx.writeAndFlush(HEART_BEAT).addListener(ChannelFutureListener.CLOSE_ON_FAILURE) ;
            }
        }
        super.userEventTriggered(ctx, evt);
    }


    /**
     * 移除已经失效的链接
     * @param ctx
     */
    private void removeChannelMap(ChannelHandlerContext ctx) {
        for( String key :ctxMap.keySet()){
            if(ctxMap.get(key)!=null && ctxMap.get(key).equals(ctx)){
                ctxMap.remove(key);
//                System.out.println("移除："+key);
                logger.info("移除机器："+key);
            }
        }
    }


    private static String objectToHexStr(Object msg) {
        ByteBuf buf = (ByteBuf) msg;
        byte[] bytes = new byte[buf.readableBytes()];
        buf.readBytes(bytes);//复制内容到字节数组bytes
        //内存泄漏 回收对象
        buf.release();
        return HexUtil.encodeHexStr(bytes);//将接收到的数据转为字符串，此字符串就是客户端发送的字符串
    }

    public static boolean sendMessage(String keyid,TcpServiceRopo tcpServiceRopo) throws Exception {
        for(Map.Entry entry : ctxMap.entrySet()){
            String mapKey = (String) entry.getKey();
            ChannelHandlerContext mapValue = (ChannelHandlerContext)entry.getValue();
            System.out.println(mapKey+":"+mapValue);
        }

        if(!ctxMap.containsKey(keyid)){
            System.out.println("链接已经关闭");
            return false;
        }
        ChannelHandlerContext ctx = ctxMap.get(keyid);
        ctx.write(Unpooled.wrappedBuffer(NumConvertUtil.hexStringToByte(tcpServiceRopo.getHexStr())));
        ctx.flush();
        return true;
    }

}
